/* 
 * Copyright [2018] [Alex/libo(liboware@gmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.ext.scanner;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import com.hyts.ext.scanner.base.ScanExecutor;
import com.hyts.ext.scanner.bean.ScanFactoryBean;
import com.hyts.ext.scanner.config.DefaultPatternMetadata;
import com.hyts.ext.scanner.exception.ScanException;
import com.hyts.ext.scanner.model.PatternParam;
import com.hyts.ext.scanner.model.ScannerParam;
import com.hyts.ext.scanner.model.ScannerResult;

/** 
 * @author LiBo/Alex
 * @see com.hyts.ext.scanner.scanner.ScanFactoryBean
 * @see com.hyts.ext.scanner.scanner.ScanExecutor<ScannerParam>
 * @version V1.0 
 */
public class DefaultScanExecutor extends ScanFactoryBean implements ScanExecutor<ScannerParam>{

    /**  
     * @constructor：ScanExecutor  
     * @param scannerParam  
     */ 
    public DefaultScanExecutor(ScannerParam scannerParam) {
	super(scannerParam);
    }

    /**  
     * @constructor：DefaultScanExecutor    
     */ 
    public DefaultScanExecutor() {
	super();
    }

    /*
     * @param param
     * @return
     * @see com.hyts.ext.scanner.scanner.ScanExecutor#scan(java.lang.Object)  
     * @exception
     */ 
    @Override
    public ScannerResult scan(ScannerParam param) {
	try {
	    return execute(param);
	} catch (IOException | ClassNotFoundException e) {
	    throw new ScanException("扫描执行失败",e);
	}
    }
    
    /**  
     * @constructor：DefaultScanExecutor  
     * @param scanPackages
     * @param scanCallClassLoader  
     */ 
    
    public ScannerResult scan(Collection<Class<?>> scanAnnotations,Class<?> scanCallClassLoader) {
	if(scanCallClassLoader == null) {
	    throw new ScanException("调用扫描的方法的ClassLoader类必须要传入!"); 
	}
	Collection<String> scanPackages = new ArrayList<String>(1) {
	    /**  
	     * @fieldName serialVersionUID
	     * @fieldType long
	     */ 
	    private static final long serialVersionUID = 1L;
	{
	    add(scanCallClassLoader.getPackage().getName()); 
	}};
	try {
	    return execute(new ScannerParam(scanPackages,scanAnnotations));
	} catch (IOException | ClassNotFoundException e) {
	    throw new ScanException("扫描执行失败",e);
	}
    }
    
    public ScannerResult scan(Class<?> scanCallClassLoader,Class<?>... scanAnnotations) {
	if(scanCallClassLoader == null) {
	    throw new ScanException("调用扫描的方法的ClassLoader类必须要传入!"); 
	}
	Collection<String> scanPackages = new ArrayList<String>(1) {
	    /**  
	     * @fieldName serialVersionUID
	     * @fieldType long
	     */ 
	    private static final long serialVersionUID = 1L;
	{
	    add(scanCallClassLoader.getPackage().getName()); 
	}};
	try {
	    return execute(new ScannerParam(scanPackages,Arrays.asList(scanAnnotations)));
	} catch (IOException | ClassNotFoundException e) {
	    throw new ScanException("扫描执行失败",e);
	}
    }
    
    public ScannerResult scan(String scanCallClassName,Class<?>... scanAnnotations) throws ClassNotFoundException {
	if(scanCallClassName == null || scanCallClassName.isEmpty()) {
   	    throw new ScanException("调用扫描的方法的ClassLoader类必须要传入!"); 
   	}
	Class<?> scanCallClassLoader = Class.forName(scanCallClassName);
   	Collection<String> scanPackages = new ArrayList<String>(1) {
   	    /**  
   	     * @fieldName serialVersionUID
   	     * @fieldType long
   	     */ 
   	    private static final long serialVersionUID = 1L;
   	{
   	    add(scanCallClassLoader.getPackage().getName()); 
   	}};
   	try {
   	    return execute(new ScannerParam(scanPackages,Arrays.asList(scanAnnotations)));
   	} catch (IOException | ClassNotFoundException e) {
   	    throw new ScanException("扫描执行失败",e);
   	}
       }
    
    /**
     * @throws ClassNotFoundException 
     * @throws IOException   
     * @param param
     * @return
     * @exception
     */ 
    @SuppressWarnings("unchecked")
    private ScannerResult execute(ScannerParam param) throws IOException, ClassNotFoundException {
	ScannerResult result = new ScannerResult();
	PatternParam patten = new PatternParam();
	if(param == null) {
	    throw new ScanException("扫描的ScannerParam参数不可以为空!，请设置扫描的包范围"); 
	}
	Collection<String> packageList = param.getScanPackages();
	Collection<Class<?>> annotationList = param.getScanAnnotations();
	if(packageList == null || packageList.isEmpty()) {
	   throw new ScanException("扫描的包不为空，请设置扫描的包范围"); 
	}
	if(annotationList == null || annotationList.isEmpty()) {
	   throw new ScanException("扫描的注解不为空，请设置扫描的注解"); 
	}
	Iterator<Class<?>> annotationIterator =  annotationList.iterator();
	List<TypeFilter> typeAnnotationFilter = new ArrayList<>();
	while(annotationIterator.hasNext()) {
	    typeAnnotationFilter.add(new AnnotationTypeFilter((Class<? extends Annotation>) annotationIterator.next(),param.isIncludeAnnotationScope(),param.isIncludeInterfaceScope()));
	}
	Iterator<String> iterator = packageList.iterator();
	while(iterator.hasNext()) {
	    String packageName = null,pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
	    packageName = iterator.next();
	    pattern = pattern
	    .concat(ClassUtils.convertClassNameToResourcePath(packageName))
	    .concat(DefaultPatternMetadata.DEFAULT_PATH_RESOURCE_PATTERN);
	Resource[] resources = patten.getPathPatternResolver()
			.getResources(pattern);
	MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(patten.getPathPatternResolver());
	for (Resource resource : resources) {
	     if (resource.isReadable()) {
		  MetadataReader reader;
		try {
		    reader = readerFactory.getMetadataReader(resource);
		    String className = reader.getClassMetadata().getClassName();
		    if (matchesEntityTypeFilter(reader, readerFactory,typeAnnotationFilter)) {
			result.offer(Class.forName(className));
		    }
		} catch (Exception e) {
		   System.out.println("出现错误!");
		}
	     }
	}
      }
	return result;
    }
    	/**
	 * 检查当前扫描到的类是否包含指定的注解
	 * 
	 * @param reader
	 * @param readerFactory
	 * @return
	 * @throws IOException
	 */
	private boolean matchesEntityTypeFilter(MetadataReader reader,
			MetadataReaderFactory readerFactory,List<TypeFilter> typeFilter) throws IOException {
		if (null != typeFilter && !typeFilter.isEmpty()) {
			for (TypeFilter filter : typeFilter) {
				if (filter.match(reader, readerFactory)) {
					return true;
				}
			}
		}
		return false;
	}

	/* 
	 * @return
	 * @see com.hyts.ext.scanner.scanner.ScanExecutor#scan()  
	 * @exception
	 */ 
	@Override
	public ScannerResult scan() {
	    try {
		return execute(this.getScannerParam());
	    } catch (ClassNotFoundException | IOException e) {
		throw new ScanException("扫描执行失败",e);
	    }
	}
}
